
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <link href='/css/styles.css' rel='stylesheet' type='text/css' />
    <link href='/images/favicon.png' rel='shortcut icon' />
    <script src='/js/jquery.min.1.4.js'></script>
    <script src='/js/app.js'></script>
    <meta content='width=device-width, minimum-scale=1.0, maximum-scale=1.0' name='viewport' />
    <title>Redis中文资料站</title>
	<meta http-equiv="description" content="redis中文资料站，下载安装redis，查找redis常用命令（commands），选择适合的redis客户端方式，配置redis主从（master-slave），阅读redis官方文档，社区里了解更多redis信息，提交redis的bug。" />
  </head>
  <body class=''>
    <script src='/js/head.js'></script>
    <div class='text'>
      <article id='topic'>
        <h1>﻿A fifteen minute introduction to Redis data types</h1>
        
        <p>As you already probably know Redis is not a plain key-value store, actually it
        is a <em>data structures server</em>, supporting different kind of values. That is,
        you can't just set strings as values of keys. All the following data types are
        supported as values:</p>
        
        <ul>
        <li>Binary-safe strings.</li>
        <li>Lists of binary-safe strings.</li>
        <li>Sets of binary-safe strings, that are collection of unique unsorted elements.
        You can think at this as a Ruby hash where all the keys are set to the 'true'
        value.</li>
        <li>Sorted sets, similar to Sets but where every element is associated to a
        floating number score. The elements are taken sorted by score. You can think
        of this as Ruby hashes where the key is the element and the value is the
        score, but where elements are always taken in order without requiring a
        sorting operation.</li>
        </ul>
        
        
        <p>It's not always trivial to grasp how this data types work and what to use in
        order to solve a given problem from the <a href="/commands">command reference</a>, so this
        document is a crash course to Redis data types and their most used patterns.</p>
        
        <p>For all the examples we'll use the <code>redis-cli</code> utility, that's a simple but
        handy command line utility to issue commands against the Redis server.</p>
        
        <h2>Redis keys</h2>
        
        <p>Redis keys are binary safe, this means that you can use any binary sequence as a
        key, from a string like "foo" to the content of a JPEG file.
        The empty string is also a valid key.</p>
        
        <p>A few other rules about keys:</p>
        
        <ul>
        <li>Too long keys are not a good idea, for instance a key of 1024 bytes is not a
        good idea not only memory-wise, but also because the lookup of the key in the
        dataset may require several costly key-comparisons.</li>
        <li>Too short keys are often not a good idea. There is little point in writing "u:1000:pwd"
        as key if you can write instead "user:1000:password", the latter is more
        readable and the added space is little compared to the space used by the
        key object itself and the value object. However it is not possible to deny
        that short keys will consume a bit less memory.</li>
        <li>Try to stick with a schema. For instance "object-type:id:field" can be a nice
        idea, like in "user:1000:password". I like to use dots for multi-words
        fields, like in "comment:1234:reply.to".</li>
        </ul>
        
        
        <h2>The string type</h2>
        
        <p>This is the simplest Redis type. If you use only this type, Redis will be
        something like a memcached server with persistence.</p>
        
        <p>Let's play a bit with the string type:</p>
        
        <pre><code>$ redis-cli set mykey "my binary safe value"&#x000A;OK&#x000A;$ redis-cli get mykey&#x000A;my binary safe value&#x000A;</code></pre>
        
        <p>As you can see using the <a href="/commands/set">SET command</a> and the <a href="/commands/get">GET
        command</a> is trivial to set values to strings and have this
        strings returned back.</p>
        
        <p>Values can be strings (including binary data) of every kind, for instance you
        can store a jpeg image inside a key. A value can't be bigger than 1 Gigabyte.</p>
        
        <p>Even if strings are the basic values of Redis, there are interesting operations
        you can perform against them. For instance one is atomic increment:</p>
        
        <pre><code>$ redis-cli set counter 100&#x000A;OK&#x000A;$ redis-cli incr counter&#x000A;(integer) 101&#x000A;$ redis-cli incr counter&#x000A;(integer) 102&#x000A;$ redis-cli incrby counter 10&#x000A;(integer) 112&#x000A;</code></pre>
        
        <p>The <a href="/commands/incr">INCR</a> command parses the string value as an integer,
        increments it by one, and finally sets the obtained value as the new string
        value. There are other similar commands like <a href="/commands/incrby">INCRBY</a>,
        <a href="commands/decr">DECR</a> and <a href="/commands/decrby">DECRBY</a>. Actually internally it's
        always the same command, acting in a slightly different way.</p>
        
        <p>What means that INCR is atomic? That even multiple clients issuing INCR against
        the same key will never incur into a race condition. For instance it can never
        happen that client 1 read "10", client 2 read "10" at the same time, both
        increment to 11, and set the new value of 11. The final value will always be of
        12 and the read-increment-set operation is performed while all the other
        clients are not executing a command at the same time.</p>
        
        <p>Another interesting operation on string is the <a href="/commands/getset">GETSET</a>
        command, that does just what its name suggests: Set a key to a new value,
        returning the old value, as result. Why this is useful? Example: you have a
        system that increments a Redis key using the <a href="/commands/incr">INCR</a> command
        every time your web site receives a new visit. You want to collect this
        information one time every hour, without losing a single key. You can GETSET
        the key assigning it the new value of "0" and reading the old value back.</p>
        
        <h2>The List type</h2>
        
        <p>To explain the List data type it's better to start with a little bit of theory,
        as the term <em>List</em> is often used in an improper way by information technology
        folks. For instance "Python Lists" are not what the name may suggest (Linked
        Lists), but them are actually Arrays (the same data type is called Array in
        Ruby actually).</p>
        
        <p>From a very general point of view a List is just a sequence of ordered
        elements: 10,20,1,2,3 is a list. But the properties of a List implemented using
        an Array are very different from the properties of a List implemented using a
        <em>Linked List</em>.</p>
        
        <p>Redis lists are implemented via Linked Lists. This means that even if you have
        millions of elements inside a list, the operation of adding a new element in
        the head or in the tail of the list is performed <em>in constant time</em>. Adding a
        new element with the <a href="/commands/lpush">LPUSH</a> command to the head of a ten
        elements list is the same speed as adding an element to the head of a 10
        million elements list.</p>
        
        <p>What's the downside? Accessing an element <em>by index</em> is very fast in lists
        implemented with an Array and not so fast in lists implemented by linked lists.</p>
        
        <p>Redis Lists are implemented with linked lists because for a database system it
        is crucial to be able to add elements to a very long list in a very fast way.
        Another strong advantage is, as you'll see in a moment, that Redis Lists can be
        taken at constant length in constant time.</p>
        
        <h2>First steps with Redis lists</h2>
        
        <p>The <a href="/commands/lpush">LPUSH</a> command adds a new element into a list, on the
        left (at the head), while the <a href="/commands/rpush">RPUSH</a> command adds a new
        element into a list, on the right (at the tail). Finally the
        <a href="/commands/lrange">LRANGE</a> command extracts ranges of elements from lists:</p>
        
        <pre><code>$ redis-cli rpush messages "Hello how are you?"&#x000A;OK&#x000A;$ redis-cli rpush messages "Fine thanks. I'm having fun with Redis"&#x000A;OK&#x000A;$ redis-cli rpush messages "I should look into this NOSQL thing ASAP"&#x000A;OK&#x000A;$ redis-cli lrange messages 0 2&#x000A;1. Hello how are you?&#x000A;2. Fine thanks. I'm having fun with Redis&#x000A;3. I should look into this NOSQL thing ASAP&#x000A;</code></pre>
        
        <p>Note that <a href="/commands/lrange">LRANGE</a> takes two indexes, the first and the last
        element of the range to return. Both the indexes can be negative to tell Redis
        to start to count from the end, so -1 is the last element, -2 is the
        penultimate element of the list, and so forth.</p>
        
        <p>As you can guess from the example above, lists can be used, for instance, in
        order to implement a chat system. Another use is as queues in order to route
        messages between different processes. But the key point is that <em>you can use
        Redis lists every time you require to access data in the same order they are
        added</em>. This will not require any SQL ORDER BY operation, will be very fast,
        and will scale to millions of elements even with a toy Linux box.</p>
        
        <p>For instance in ranking systems like the social news reddit.com you can add
        every new submitted link into a List, and with <a href="/commands/lrange">LRANGE</a> it's
        possible to paginate results in a trivial way.</p>
        
        <p>In a blog engine implementation you can have a list for every post, where to
        push blog comments, and so forth.</p>
        
        <h2>Pushing IDs instead of the actual data in Redis lists</h2>
        
        <p>In the above example we pushed our "objects" (simply messages in the example)
        directly inside the Redis list, but this is often not the way to go, as objects
        can be referenced in multiple times: in a list to preserve their chronological
        order, in a Set to remember they are about a specific category, in another list
        but only if this object matches some kind of requisite, and so forth.</p>
        
        <p>Let's return back to the reddit.com example. A more credible pattern for adding
        submitted links (news) to the list is the following:</p>
        
        <pre><code>$ redis-cli incr next.news.id&#x000A;(integer) 1&#x000A;$ redis-cli set news:1:title "Redis is simple"&#x000A;OK&#x000A;$ redis-cli set news:1:url "http://code.google.com/p/redis"&#x000A;OK&#x000A;$ redis-cli lpush submitted.news 1&#x000A;OK&#x000A;</code></pre>
        
        <p>We obtained an unique incremental ID for our news object just incrementing a
        key, then used this ID to create the object setting a key for every field in
        the object. Finally the ID of the new object was pushed on the <em>submitted.news</em>
        list.</p>
        
        <p>This is just the start. Check the <a href="/commands">command reference</a> and read about
        all the other list related commands. You can remove elements, rotate lists, get
        and set elements by index, and of course retrieve the length of the list with
        <a href="/commands/llen">LLEN</a>.</p>
        
        <h2>Redis Sets</h2>
        
        <p>Redis Sets are unordered collection of binary-safe strings. The
        <a href="/commands/sadd">SADD</a> command adds a new element to a set. It's also possible
        to do a number of other operations against sets like testing if a given element
        already exists, performing the intersection, union or difference between
        multiple sets and so forth. An example is worth 1000 words:</p>
        
        <pre><code>$ redis-cli sadd myset 1&#x000A;(integer) 1&#x000A;$ redis-cli sadd myset 2&#x000A;(integer) 1&#x000A;$ redis-cli sadd myset 3&#x000A;(integer) 1&#x000A;$ redis-cli smembers myset&#x000A;1. 3&#x000A;2. 1&#x000A;3. 2&#x000A;</code></pre>
        
        <p>I added three elements to my set and told Redis to return back all the
        elements. As you can see they are not sorted.</p>
        
        <p>Now let's check if a given element exists:</p>
        
        <pre><code>$ redis-cli sismember myset 3&#x000A;(integer) 1&#x000A;$ redis-cli sismember myset 30&#x000A;(integer) 0&#x000A;</code></pre>
        
        <p>"3" is a member of the set, while "30" is not. Sets are very good for
        expressing relations between objects. For instance we can easily use Redis Sets
        in order to implement tags.</p>
        
        <p>A simple way to model this is to have, for every object you want to tag, a Set
        with all the IDs of the tags associated with the object, and for every tag that
        exists, a Set of of all the objects tagged with this tag.</p>
        
        <p>For instance if our news ID 1000 is tagged with tag 1,2,5 and 77, we can
        specify the following two Sets:</p>
        
        <pre><code>$ redis-cli sadd news:1000:tags 1&#x000A;(integer) 1&#x000A;$ redis-cli sadd news:1000:tags 2&#x000A;(integer) 1&#x000A;$ redis-cli sadd news:1000:tags 5&#x000A;(integer) 1&#x000A;$ redis-cli sadd news:1000:tags 77&#x000A;(integer) 1&#x000A;$ redis-cli sadd tag:1:objects 1000&#x000A;(integer) 1&#x000A;$ redis-cli sadd tag:2:objects 1000&#x000A;(integer) 1&#x000A;$ redis-cli sadd tag:5:objects 1000&#x000A;(integer) 1&#x000A;$ redis-cli sadd tag:77:objects 1000&#x000A;(integer) 1&#x000A;</code></pre>
        
        <p>To get all the tags for a given object is trivial:</p>
        
        <pre><code>$ redis-cli smembers news:1000:tags&#x000A;1. 5&#x000A;2. 1&#x000A;3. 77&#x000A;4. 2&#x000A;</code></pre>
        
        <p>But there are other non trivial operations that are still easy to implement
        using the right Redis commands. For instance we may want the list of all the
        objects having as tags 1, 2, 10, and 27 at the same time. We can do this using
        the <a href="/commands/sinter">SINTER</a> that performs the intersection between different
        sets. So in order to reach our goal we can just use:</p>
        
        <pre><code>$ redis-cli sinter tag:1:objects tag:2:objects tag:10:objects tag:27:objects&#x000A;... no result in our dataset composed of just one object ;) ...&#x000A;</code></pre>
        
        <p>Look at the <a href="/commands">command reference</a> to discover other Set related
        commands, there are a bunch of interesting ones. Also make sure to check the
        <a href="/commands/sort">SORT</a> command as both Redis Sets and Lists are sortable.</p>
        
        <h2>A digression: How to get unique identifiers for strings</h2>
        
        <p>In our tags example we showed tag IDs without to mention how this IDs can be
        obtained. Basically for every tag added to the system, you need an unique
        identifier. You also want to be sure that there are no race conditions if
        multiple clients are trying to add the same tag at the same time. Also, if a
        tag already exists, you want its ID returned, otherwise a new unique ID should
        be created and associated to the tag.</p>
        
        <p>Redis 1.4 will add the Hash type. With it it will be trivial to associate
        strings with unique IDs, but how to do this today with the current commands
        exported by Redis in a reliable way?</p>
        
        <p>Our first attempt (that is broken) can be the following. Let's suppose we want
        to get an unique ID for the tag "redis":</p>
        
        <ul>
        <li>In order to make this algorithm binary safe (they are just tags but think to
        utf8, spaces and so forth) we start performing the SHA1 sum of the tag.
        SHA1(redis) = b840fc02d524045429941cc15f59e41cb7be6c52.</li>
        <li>Let's check if this tag is already associated with an unique ID with the
        command <em>GET tag:b840fc02d524045429941cc15f59e41cb7be6c52:id</em>.</li>
        <li>If the above GET returns an ID, return it back to the user. We already have
        the unique ID.</li>
        <li>Otherwise... create a new unique ID with <em>INCR next.tag.id</em> (assume it
        returned 123456).</li>
        <li>Finally associate this new ID to our tag with <em>SET
        tag:b840fc02d524045429941cc15f59e41cb7be6c52:id 123456</em> and return the new ID
        to the caller.</li>
        </ul>
        
        
        <p>Nice. Or better.. broken! What about if two clients perform this commands at
        the same time trying to get the unique ID for the tag "redis"? If the timing is
        right they'll both get <em>nil</em> from the GET operation, will both increment the
        <em>next.tag.id</em> key and will set two times the key. One of the two clients will
        return the wrong ID to the caller. To fix the algorithm is not hard
        fortunately, and this is the sane version:</p>
        
        <ul>
        <li>In order to make this algorithm binary safe (they are just tags but think to
        utf8, spaces and so forth) we start performing the SHA1 sum of the tag.
        SHA1(redis) = b840fc02d524045429941cc15f59e41cb7be6c52.</li>
        <li>Let's check if this tag is already associated with an unique ID with the
        command <em>GET tag:b840fc02d524045429941cc15f59e41cb7be6c52:id</em>.</li>
        <li>If the above GET returns an ID, return it back to the user. We already have
        the unique ID.</li>
        <li>Otherwise... create a new unique ID with <em>INCR next.tag.id</em> (assume it
        returned 123456).</li>
        <li>Finally associate this new ID to our tag with <em>SETNX
        tag:b840fc02d524045429941cc15f59e41cb7be6c52:id 123456</em>. By using SETNX if a
        different client was faster than this one the key wil not be setted. Not
        only, SETNX returns 1 if the key is set, 0 otherwise. So... let's add a final
        step to our computation.</li>
        <li>If SETNX returned 1 (We set the key) return 123456 to the caller, it's our
        tag ID, otherwise perform <em>GET
        tag:b840fc02d524045429941cc15f59e41cb7be6c52:id</em> and return the value to the
        caller.</li>
        </ul>
        
        
        <h2>Sorted sets</h2>
        
        <p>Sets are a very handy data type, but... they are a bit too unsorted in order to
        fit well for a number of problems ;) This is why Redis 1.2 introduced Sorted
        Sets. They are very similar to Sets, collections of binary-safe strings, but
        this time with an associated score, and an operation similar to the List LRANGE
        operation to return items in order, but working against Sorted Sets, that is,
        the <a href="/commands/zrange">ZRANGE</a> command.</p>
        
        <p>Basically Sorted Sets are in some way the Redis equivalent of Indexes in the
        SQL world. For instance in our reddit.com example above there was no mention
        about how to generate the actual home page with news raked by user votes and
        time. We'll see how sorted sets can fix this problem, but it's better to start
        with something simpler, illustrating the basic working of this advanced data
        type. Let's add a few selected hackers with their year of birth as "score".</p>
        
        <pre><code>$ redis-cli zadd hackers 1940 "Alan Kay"&#x000A;(integer) 1&#x000A;$ redis-cli zadd hackers 1953 "Richard Stallman"&#x000A;(integer) 1&#x000A;$ redis-cli zadd hackers 1965 "Yukihiro Matsumoto"&#x000A;(integer) 1&#x000A;$ redis-cli zadd hackers 1916 "Claude Shannon"&#x000A;(integer) 1&#x000A;$ redis-cli zadd hackers 1969 "Linus Torvalds"&#x000A;(integer) 1&#x000A;$ redis-cli zadd hackers 1912 "Alan Turing"&#x000A;(integer) 1&#x000A;</code></pre>
        
        <p>For sorted sets it's a joke to return these hackers sorted by their birth year
        because actually <em>they are already sorted</em>. Sorted sets are implemented via a
        dual-ported data structure containing both a skip list and an hash table, so
        every time we add an element Redis performs an <span class="math">O(log(N)) </span>operation. That's
        good, but when we ask for sorted elements Redis does not have to do any work at
        all, it's already all sorted:</p>
        
        <pre><code>$ redis-cli zrange hackers 0 -1&#x000A;1. Alan Turing&#x000A;2. Claude Shannon&#x000A;3. Alan Kay&#x000A;4. Richard Stallman&#x000A;5. Yukihiro Matsumoto&#x000A;6. Linus Torvalds&#x000A;</code></pre>
        
        <p>Didn't know that Linus was younger than Yukihiro btw ;)</p>
        
        <p>Anyway I want to order these elements the other way around, using
        <a href="/commands/zrevrange">ZREVRANGE</a> instead of <a href="/commands/zrange">ZRANGE</a> this
        time:</p>
        
        <pre><code>$ redis-cli zrevrange hackers 0 -1&#x000A;1. Linus Torvalds&#x000A;2. Yukihiro Matsumoto&#x000A;3. Richard Stallman&#x000A;4. Alan Kay&#x000A;5. Claude Shannon&#x000A;6. Alan Turing&#x000A;</code></pre>
        
        <p>A very important note, ZSets have just a "default" ordering but you are still
        free to call the <a href="/commands/sort">SORT</a> command against sorted sets to get a
        different ordering (but this time the server will waste CPU). An alternative
        for having multiple orders is to add every element in multiple sorted sets at
        the same time.</p>
        
        <h2>Operating on ranges</h2>
        
        <p>Sorted sets are more powerful than this. They can operate on ranges. For
        instance let's try to get all the individuals that were born up to the 1950. We
        use the <a href="/commands/zrangebyscore">ZRANGEBYSCORE</a> command to do it:</p>
        
        <pre><code>$ redis-cli zrangebyscore hackers -inf 1950&#x000A;1. Alan Turing&#x000A;2. Claude Shannon&#x000A;3. Alan Kay&#x000A;</code></pre>
        
        <p>We asked Redis to return all the elements with a score between negative
        infinity and 1950 (both extremes are included).</p>
        
        <p>It's also possible to remove ranges of elements. For instance let's remove all
        the hackers born between 1940 and 1960 from the sorted set:</p>
        
        <pre><code>$ redis-cli zremrangebyscore hackers 1940 1960&#x000A;(integer) 2&#x000A;</code></pre>
        
        <p><a href="/commands/zremrangebyscore">ZREMRANGEBYSCORE</a> is not the best command name,
        but it can be very useful, and returns the number of removed elements.</p>
        
        <h2>Back to the Reddit example</h2>
        
        <p>For the last time, back to the Reddit example. Now we have a decent plan to
        populate a sorted set in order to generate the home page. A sorted set can
        contain all the news that are not older than a few days (we remove old entries
        from time to time using ZREMRANGEBYSCORE). A background job gets all the
        elements from this sorted set, get the user votes and the time of the news, and
        compute the score to populate the <em>reddit.home.page</em> sorted set with the news
        IDs and associated scores. To show the home page we just have to perform a
        blazingly fast call to ZRANGE.</p>
        
        <p>From time to time we'll remove too old news from the <em>reddit.home.page</em> sorted
        set as well in order for our system to work always against a limited set of
        news.</p>
        
        <h2>Updating the scores of a sorted set</h2>
        
        <p>Just a final note before wrapping up this tutorial. Sorted sets scores can be
        updated at any time. Just calling again ZADD against an element already
        included in the sorted set will update its score (and position) in <span class="math">O(log(N)),</span>
        so sorted sets are suitable even when there are tons of updates.</p>
        
        <p>This tutorial is in no way complete, this is just the basics to get started
        with Redis, read the <a href="/commands">command reference</a> to discover a lot more.</p>
        
        <p>Thanks for reading. Salvatore.</p>
      </article>
    </div>
    <div class='text' id='comments'>
      <div id='disqus_thread'></div>
      <script type='text/javascript'>
        //<![CDATA[
          var disqus_shortname = 'rediscn';
          
          /* * * DON'T EDIT BELOW THIS LINE * * */
          (function() {
            var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
              dsq.src = 'http://' + disqus_shortname + '.disqus.com/embed.js';
              (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
          })();
        //]]>
      </script>
      <a class='dsq-brlink' href='http://disqus.com'>
        Comments powered by
        <span class='logo-disqus'>
          Disqus
        </span>
      </a>
    </div>
    <script src='/js/foot.js'></script>
  </body>
</html>
